package com.open.source.demo;

import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;

import com.open.source.javassist.ClassMaker;
import com.open.source.javassist.ModifierAccess;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.springframework.util.ResourceUtils;

/**
 * @author ZonLen since on 2022/3/23 下午11:25
 */
public class GenerateClass {

  @Size(max = 10, min = 2, message = "aa")
  public String aa;

  public static void main(String[] args)
      throws CannotCompileException, NotFoundException, IOException, NoSuchFieldException, InvocationTargetException, IllegalAccessException {
    final Class<?> aClass =
        maker();
//        getaClass();
    System.out.println(aClass);
  }

  private static Class<?> getaClass()
      throws CannotCompileException, NotFoundException, IOException {
    ClassPool pool = ClassPool.getDefault();
    CtClass ctClass = pool.makeClass("com.zhanglong.Person");

        /*
        为Person类添加两个变量：
                                private String name;
                                private Integer age;
         */
    CtField name = new CtField(pool.get(String.class.getCanonicalName()), "name", ctClass);
    name.setModifiers(Modifier.PRIVATE);
    ctClass.addField(name);
    CtField age = new CtField(pool.get(Integer.class.getCanonicalName()), "age", ctClass);
    age.setModifiers(Modifier.PRIVATE);
    ctClass.addField(age);
    CtField birthday = new CtField(pool.get(Date.class.getCanonicalName()), "birthday", ctClass);
    birthday.setModifiers(Modifier.PRIVATE);
    ctClass.addField(birthday);
    //关于这段操作我也不太懂，都是零零散散地收集网上看到的，有大佬懂的话希望帮我解答解答~
    ClassFile classFile = ctClass.getClassFile();
    ConstPool constPool = classFile.getConstPool();
    AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool,
        AnnotationsAttribute.visibleTag);
    //创建要添加的注解
    Annotation jsonFileAnnotation = new Annotation(NotBlank.class.getCanonicalName(), constPool);
    //设置注解中的属性和值
    jsonFileAnnotation.addMemberValue("message", new StringMemberValue("xxx不能为空", constPool));
    //把这个注解放到一个AnnotationsAttribute对象里面
    annotationsAttribute.addAnnotation(jsonFileAnnotation);
    //把这个对象怼到要打上这个注解的字段/类上面
    age.getFieldInfo().addAttribute(annotationsAttribute);
    //类注解
    classFile.addAttribute(annotationsAttribute);

    //添加getter setter方法
    ctClass.addMethod(CtNewMethod.setter("setName", name));
    ctClass.addMethod(CtNewMethod.setter("setAge", age));
    final CtMethod getName = CtNewMethod.getter("getName", name);
    getName.getMethodInfo().addAttribute(annotationsAttribute);
    ctClass.addMethod(getName);
    final CtMethod getAge = CtNewMethod.getter("getAge", age);
    getAge.getMethodInfo().addAttribute(annotationsAttribute);
    ctClass.addMethod(getAge);

    //添加toString方法
    CtMethod toStringMethod = new CtMethod(pool.get("java.lang.String"), "toString", null, ctClass);
    String builder = "return \"Person{\" +\n" +
        "                \"name='\" + name + '\\'' +\n" +
        "                \", age='\" + age + '\\'' +\n" +
        "                '}';";
    toStringMethod.setBody(builder);
    ctClass.addMethod(toStringMethod);
    ctClass.setSuperclass(pool.get(HighAnimal.class.getName()));
    ctClass.writeFile(ResourceUtils.getURL(CLASSPATH_URL_PREFIX).getPath());
    return ctClass.toClass();
  }


  private static Class<?> maker()
      throws CannotCompileException, NotFoundException, IOException {
    final ClassMaker classMaker = ClassMaker.create("com.zhanglong.Demo1")
        ._extends(Demo.class.getSuperclass())
        ._implements(Demo.class.getInterfaces())
        .newMethod(m -> m.methodName("run")
            ._annotation(a -> a.annotationClass(NotNull.class).annotationAttr("message", "xxx不能为空")
                .buildAnnotation())
            .returnType(Date.class)
            .parameters(String.class, Date.class, Long.class)
            .exceptions(Exception.class)
            .body("{ System.out.println($1); return $2; }")
            .buildMethod());
    for (Field field : Demo.class.getDeclaredFields()) {
      classMaker.newField(f -> f
          .modifierAccess(ModifierAccess.convert(field.getModifiers()))
          .filedName(field.getName())
          .filedType(field.getType())
          ._annotations(field.getAnnotations(), (annotation, annotationMaker) -> annotationMaker
              .annotationClass(annotation.annotationType()).annotationAttr(annotation)
              .buildAnnotation())
          .generateGet().generateSet()
          .buildField());
    }
    return classMaker.buildClass();
//    return ClassMaker.create("com.zhanglong.Person1")
//        ._extends(HighAnimal.class)
//        ._implements(Animal.class)
//        ._annotation(a -> a.annotationClass(NotBlank.class).annotationAttr("message", "xxx不能为空")
//            .buildAnnotation())
//        ._annotation(a -> a.annotationClass(NotNull.class).annotationAttr("message", "xxx不能为空")
//            .buildAnnotation())
//        .newField(f -> f.filedName("a").filedType(String.class)
//            ._annotation(a -> a.annotationClass(NotBlank.class).annotationAttr("message", "xxx不能为空")
//                .buildAnnotation())
//            ._annotation(a -> a.annotationClass(Size.class).annotationAttr("message", "xxx不能为空")
//                .buildAnnotation())
//            .generateGet().generateSet()
//            .buildField())
//        .newField(f -> f.filedName("b").filedType(Long.class)
//            ._annotation(a -> a.annotationClass(NotNull.class).annotationAttr("message", "xxx不能为空")
//                .buildAnnotation())
//            .generateGet().generateSet()
//            .buildField())
//        .newField(f -> f.filedName("c").filedType(Date.class)
//            ._annotation(a -> a.annotationClass(NotNull.class).annotationAttr("message", "xxx不能为空")
//                .buildAnnotation())
//            .generateGet().generateSet()
//            .buildField())
//        .newMethod(m -> m.methodName("run")
//            ._annotation(a -> a.annotationClass(NotNull.class).annotationAttr("message", "xxx不能为空")
//                .buildAnnotation())
//            .returnType(Date.class)
//            .parameters(String.class, Date.class, Long.class)
//            .exceptions(Exception.class)
//            .body("{ System.out.println($1); return $2; }")
//            .buildMethod())
//        .buildClass();
  }

}
